#!/usr/bin/env python3
"""Updates version.yml and CHANGELOG.md for next version, then
launch the `git citool` GUI to create a commit of it.
"""

import os
import re
import sys
from contextlib import contextmanager
from typing import Generator
from shutil import which
import argparse
import subprocess

from lib import re_ga, re_ea, get_gh_repo, vX, vY
from lib.uiutil import Checker, CheckResult, run, check_command
from lib.uiutil import run_txtcapture as run_capture
from lib.start_release_updates import update_changelog_date
from lib.gitutil import branch_exists, has_open_pr


PR_MESSAGE = """
All commits that are going out in {next_ver} release **must** be on {pr_branch}.
This will allow the appropriate CI to run.

Reviewers, please review the changelog and commits in this branch to make sure everything that needs to go out in the release is on this branch.
"""


def main(next_ver: str, commit: bool = True) -> int:
    """Starts a release train.
    This script will create a changelog entry and create PRs of applicable.
    """
    m = re_ga.match(next_ver)
    if not m:
        m = re_ea.match(next_ver)
    assert m

    if which("gh") is None:
        print("gh tool is not installed.")
        print("Please install the tool and rerun this script:")
        print("https://github.com/cli/cli#installation")
        return 1

    if not os.getenv("GITHUB_TOKEN"):
        run(["gh", "auth", "login"])

    print()
    print(f'Doing basic updates for v{next_ver}...')
    print()

    # This context manager and check function are pretty much just to produce
    # a nice list of steps...

    checker = Checker()

    @contextmanager
    def check(name: str) -> Generator[CheckResult, None, None]:
        with checker.check(name) as subcheck:
            yield subcheck
    workbranch = ''
    base_branch = ''
    current_release_branch = f"rel/v{next_ver}"
    release_branch = f"release/v{m[vX]}.{m[vY]}"
    check_command(["git", "fetch", "--all"])

    workbranch = current_release_branch
    base_branch = release_branch
    if branch_exists(current_release_branch):
        with check(f"Checking out {current_release_branch}"):
            run(["git", "checkout", current_release_branch])

        if branch_exists("origin/" + current_release_branch):
            with check(f"Bringing our branch it up to date with origin/{release_branch}"):
                run(["git", "pull", "origin", current_release_branch])
    else:
        with check(f"Creating {current_release_branch}"):
            run(["git", "checkout", "-b", current_release_branch])
    if not checker.ok:
        return 1
    run(["git", "merge", f"origin/{release_branch}"])
    with check(f"Checking for clean branch with no conflicts..."):
        out = run_capture(["git", "status", "--porcelain"])
        if out:
            raise AssertionError(f"Merge conflicts on {current_release_branch}. Resolve these, then rerun this scrip")
    if not checker.ok:
        return 1

    with check(f"Updating CHANGELOG.md with {next_ver}..."):
        update_changelog_date(next_ver)
    if not checker.ok:
        return 1
    if not commit:
        print('not committing')
        return 0

    out = run_capture(["git", "status", "--porcelain"])
    if not out:
        print("No changes needed, all good.")
        return 0

    with check(f"Committing changes..."):
        gitdir = run_capture(['git', 'rev-parse', '--git-dir'])
        with open(os.path.join(gitdir, 'GITGUI_MSG'), 'w') as msgfile:
            msgfile.write(f"Update for v{next_ver}\n")
        if os.getenv("AMBASSADOR_RELENG_NO_GUI"):
            run(['git', 'commit', '-am', f'Update for v{next_ver}'])
        else:
            run(['git', 'citool'])
        run(["git", "push", "-u", "origin", workbranch])
    if not checker.ok:
        return 1
    if has_open_pr(get_gh_repo(), base_branch, current_release_branch):
        run(["gh", "pr", "view", workbranch, "--repo", get_gh_repo()])
        return 0
    pr_message = f"[v{next_ver}] Release Branch"
    pr_body = PR_MESSAGE.format(next_ver=next_ver, pr_branch=current_release_branch)
    if not checker.ok:
        return 1

    with check(f"Creating PR for {workbranch}"):
        run(["gh", "pr", "create",
                "--repo", get_gh_repo(),
                "--base", base_branch,
                "--title", pr_message,
                "--body", pr_body])

    if checker.ok:
        if commit:
            print(f'Update complete. CI triggered on the PR will generate RC artifacts to test')
            print(f'Pushes to this branch will generate new RCs')
            if os.getenv("AMBASSADOR_RELENG_NO_GUI"):
                run(["gh", "pr", "view", workbranch, "--repo", get_gh_repo()])
            else:
                run(["gh", "pr", "view", workbranch, "--repo", get_gh_repo(), "--web"])
        return 0
    else:
        return 1


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Start a release train')

    parser.add_argument('--next-version', help='Next version to prep for', required=True)
    parser.add_argument('--no-commit', dest='commit', action='store_false', default=True)

    args = parser.parse_args()

    if not re_ga.match(args.next_version) and not re_ea.match(args.next_version):
        sys.stderr.write(f"{os.path.basename(sys.argv[0])}: Version must match X.Y.Z(-ea)?\n")
        sys.exit(2)

    sys.exit(main(
        next_ver=args.next_version,
        commit=args.commit
    ))
